/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "nweb_inputmethod_handler.h"

#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "cef/include/cef_task.h"
#include "content/public/browser/browser_thread.h"
#include "libcef/browser/thread_util.h"
#include "ohos_adapter_helper.h"
#include "res_sched_client_adapter.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"

namespace OHOS::NWeb {

static constexpr char16_t DEL_CHAR = 127;

class OnTextChangedListenerImpl : public IMFTextListenerAdapter {
 public:
  OnTextChangedListenerImpl(NWebInputMethodHandler* handler)
      : handler_(handler) {}
  ~OnTextChangedListenerImpl() = default;

  // All following listenser callbacks should invoke call on UI thread
  void InsertText(const std::u16string& text) override {
    if (text.size() == 1 && text.front() == '\n') {
      return;
    }
    handler_->InsertText(text);
  }

  void DeleteForward(int32_t length) override {
    handler_->DeleteForward(length);
  }

  void DeleteBackward(int32_t length) override {
    handler_->DeleteBackward(length);
  }

  void SendKeyEventFromInputMethod() override {
    LOG(INFO) << "NWebInputMethodHandler::SendKeyEventFromInputMethod";
  }

  void SendKeyboardStatus(const IMFAdapterKeyboardStatus& status) override {
    if (status == IMFAdapterKeyboardStatus::SHOW) {
      handler_->SetIMEStatus(true);
    } else if (status == IMFAdapterKeyboardStatus::HIDE) {
      handler_->SetIMEStatus(false);
    }
  }

  void SendFunctionKey(const IMFAdapterFunctionKey& functionKey) override {
    handler_->SendEnterKeyEvent();
  }

  void SetKeyboardStatus(bool status) override {
    handler_->SetIMEStatus(status);
  }

  void MoveCursor(const IMFAdapterDirection direction) override {
    if (direction == IMFAdapterDirection::NONE) {
      LOG(ERROR) << "NWebInputMethodHandler::MoveCursor got none direction";
      return;
    }
    handler_->MoveCursor(direction);
  }

  void HandleSetSelection(int32_t start, int32_t end) override{};
  void HandleExtendAction(int32_t action) override{};
  void HandleSelect(int32_t keyCode, int32_t cursorMoveSkip) override{};

  int32_t GetTextIndexAtCursor() override {
    return handler_->GetTextIndexAtCursor();
  }

  std::u16string GetLeftTextOfCursor(int32_t number) override {
    return handler_->GetLeftTextOfCursor(number);
  }

  std::u16string GetRightTextOfCursor(int32_t number) override {
    return handler_->GetRightTextOfCursor(number);
  }

 private:
  NWebInputMethodHandler* handler_;
};

class InputMethodTask : public CefTask {
 public:
  explicit InputMethodTask(base::OnceClosure closure)
      : closure_(std::move(closure)) {}

  InputMethodTask(const InputMethodTask&) = delete;
  InputMethodTask& operator=(const InputMethodTask&) = delete;

  virtual void Execute() override {
    std::move(closure_).Run();
    // closure_.Reset();
  }

 private:
  base::OnceClosure closure_;

  IMPLEMENT_REFCOUNTING(InputMethodTask);
};

NWebInputMethodHandler::NWebInputMethodHandler()
    : selected_from_(0), selected_to_(0) {
  inputmethod_adapter_ = OhosAdapterHelper::GetInstance().CreateIMFAdapter();
  if (inputmethod_adapter_ == nullptr) {
    LOG(ERROR) << "inputmethod_adapter_ create failed";
  }
}

NWebInputMethodHandler::~NWebInputMethodHandler() {}

uint32_t NWebInputMethodHandler::lastAttachNWebId_ = 0;

IMFAdapterCursorInfo NWebInputMethodHandler::GetCursorInfo() {
  IMFAdapterCursorInfo cursorInfo {
        .left = (focus_rect_.x + focus_rect_.width) * device_pixel_ratio_ +
                offset_x_,
        .top = focus_rect_.y * device_pixel_ratio_ + offset_y_,
        .width = focus_rect_.width * device_pixel_ratio_,
        .height = focus_rect_.height * device_pixel_ratio_};
    LOG(DEBUG) << "NWebInputMethodHandler::Attach cursorInfo.left = "
                << cursorInfo.left << ", cursorInfo.top = " << cursorInfo.top
                << ", cursorInfo.width = " << cursorInfo.width
                << ", cursorInfo.height = " << cursorInfo.height;
  return cursorInfo;
}

IMFAdapterTextInputType TextInputTypeToIMFAdapter(cef_text_input_type_t type) {
  switch (type) {
    case CEF_TEXT_INPUT_TYPE_TEXT:
      return IMFAdapterTextInputType::TEXT;
    case CEF_TEXT_INPUT_TYPE_PASSWORD:
      return IMFAdapterTextInputType::VISIBLE_PASSWORD;
    case CEF_TEXT_INPUT_TYPE_EMAIL:
      return IMFAdapterTextInputType::EMAIL_ADDRESS;
    case CEF_TEXT_INPUT_TYPE_NUMBER:
      return IMFAdapterTextInputType::NUMBER;
    case CEF_TEXT_INPUT_TYPE_TELEPHONE:
      return IMFAdapterTextInputType::PHONE;
    case CEF_TEXT_INPUT_TYPE_URL:
      return IMFAdapterTextInputType::URL;
    default:
      return IMFAdapterTextInputType::TEXT;
  }
}

void NWebInputMethodHandler::Attach(CefRefPtr<CefBrowser> browser,
                                    bool show_keyboard,
                                    cef_text_input_type_t input_type) {
  show_keyboard_ = show_keyboard;
  input_mode_ = TextInputTypeToIMFAdapter(input_type);
  composing_text_.clear();
  browser_ = browser;
  if (inputmethod_adapter_ == nullptr) {
    LOG(ERROR) << "inputmethod_adapter_ is nullptr";
    return;
  }
  if (inputmethod_listener_ == nullptr) {
    inputmethod_listener_ = std::make_shared<OnTextChangedListenerImpl>(this);
  }

  IMFAdapterInputAttribute inputAttribute = { .inputPattern = static_cast<int32_t>(input_mode_),
        .enterKeyType = static_cast<int32_t>(IMFAdapterEnterKeyType::DONE) };

  IMFAdapterCursorInfo cursorInfo = GetCursorInfo();

  IMFAdapterTextConfig textConfig = { .inputAttribute = inputAttribute, .cursorInfo = cursorInfo };
  if (!show_keyboard_ && isAttached_) {
    LOG(ERROR) << "do not need attach";
    return;
  }
  if (!inputmethod_adapter_->Attach(inputmethod_listener_, show_keyboard_, textConfig)) {
    LOG(ERROR) << "inputmethod_adapter_ attach failed";
    return;
  }
  isAttached_ = true;
  lastAttachNWebId_ = nweb_id_;

  if (focus_status_ && focus_rect_status_) {
    if (inputmethod_adapter_) {
      inputmethod_adapter_->OnCursorUpdate(cursorInfo);
    }
  }
}

bool NWebInputMethodHandler::Reattach(uint32_t nwebId, ReattachType type) {
  nweb_id_ = nwebId;
  if (type == ReattachType::FROM_CONTINUE) {
    if (!isNeedReattachOncontinue_ || !is_editable_node_) {
      LOG(INFO) << "don't need reattach input method";
      return false;
    }
    isNeedReattachOncontinue_ = false;
  }

  if (type == ReattachType::FROM_ONFOCUS) {
    if (!isNeedReattachOnfocus_ || !is_editable_node_) {
      LOG(INFO) << "ReAttchOnfocus, don't need reattach input method";
      return false;
    }
    isNeedReattachOnfocus_ = false;
  }

  LOG(INFO) << "need to reattach input method, show_keyboard_ = "
            << show_keyboard_ << ", \
    reattach type = "
            << static_cast<int>(type);
  composing_text_.clear();
  if (inputmethod_listener_ == nullptr) {
    inputmethod_listener_ = std::make_shared<OnTextChangedListenerImpl>(this);
  }

  if (!show_keyboard_ && !isAttached_) {
    inputmethod_adapter_->HideTextInput();
    inputmethod_adapter_->Close();
    return false;
  }
  IMFAdapterInputAttribute inputAttribute = { .inputPattern = static_cast<int32_t>(input_mode_),
        .enterKeyType = static_cast<int32_t>(IMFAdapterEnterKeyType::DONE) };

  IMFAdapterCursorInfo cursorInfo = GetCursorInfo();

  IMFAdapterTextConfig textConfig = { .inputAttribute = inputAttribute, .cursorInfo = cursorInfo };
  if (!show_keyboard_ && isAttached_) {
    LOG(ERROR) << "do not need attach";
    return true;
  }
  if (!inputmethod_adapter_->Attach(inputmethod_listener_, show_keyboard_, textConfig)) {
    LOG(ERROR) << "inputmethod_adapter_ attach failed";
    return false;
  }
  isAttached_ = true;
  lastAttachNWebId_ = nwebId;

  return show_keyboard_;
}

void NWebInputMethodHandler::ShowTextInput() {
  LOG(INFO) << "NWebInputMethodHandler::ShowTextInput";
}

void NWebInputMethodHandler::HideTextInput(uint32_t nwebId, HideTextinputType hideType) {
  if (inputmethod_adapter_ == nullptr) {
    LOG(ERROR) << "inputmethod_adapter_ is nullptr";
    return;
  }
  if (!isAttached_) {
    LOG(INFO) << "keyboard is not attach";
    if (hideType != HideTextinputType::FROM_ONPAUSE) {
      LOG(INFO) << "not from switch front and background, ingnore";
      return;
    }
    auto nowTime = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> diff =
        std::chrono::duration_cast<std::chrono::milliseconds>(
            nowTime - lastCloseInputMethodTime_);
    // 100: received another hide textinput event in 100ms, we set
    // isNeedReattachOncontinue_ flag.
    if (diff.count() < 100) {
      isNeedReattachOncontinue_ = true;
      LOG(INFO) << "set isNeedReattachOncontinue_ flag, diff = " << diff.count()
                << "ms";
    }
    return;
  }

  if (lastAttachNWebId_ == 0 || lastAttachNWebId_ == nwebId ||
      hideType == HideTextinputType::FROM_KERNEL) {
    LOG(INFO) << "need to hide text input, hidetype = "
      << static_cast<int>(hideType);
    inputmethod_adapter_->HideTextInput();
    inputmethod_adapter_->Close();
  }

  lastCloseInputMethodTime_ = std::chrono::high_resolution_clock::now();
  isAttached_ = false;
  if (hideType == HideTextinputType::FROM_ONPAUSE) {
    isNeedReattachOncontinue_ = true;
  }

  if (hideType == HideTextinputType::FROM_ONBLUR) {
    isNeedReattachOnfocus_ = true;
  }
}

void NWebInputMethodHandler::OnTextSelectionChanged(
    CefRefPtr<CefBrowser> browser,
    const CefString& selected_text,
    const CefRange& selected_range) {
  if (ime_text_composing_ && !composing_text_.empty()) {
    if (browser_ != nullptr && browser_->GetHost() != nullptr) {
      browser_->GetHost()->ImeFinishComposingText(false);
    }
  }
  selected_text_ = selected_text.ToString16();
  selected_from_ = selected_range.from;
  selected_to_ = selected_range.to;
  ime_text_composing_ = false;
  composing_text_.clear();
}

void NWebInputMethodHandler::OnCursorUpdate(const CefRect& rect) {
  focus_rect_ = rect;
  focus_rect_status_ = true;
  if (focus_status_) {
    IMFAdapterCursorInfo cursorInfo{
        .left = (rect.x + rect.width) * device_pixel_ratio_ + offset_x_,
        .top = rect.y * device_pixel_ratio_ + offset_y_,
        .width = rect.width * device_pixel_ratio_,
        .height = rect.height * device_pixel_ratio_};
    LOG(DEBUG) << "NWebInputMethodHandler::OnCursorUpdate cursorInfo.left = "
               << cursorInfo.left << ", cursorInfo.top = " << cursorInfo.top
               << ", cursorInfo.width = " << cursorInfo.width
               << ", cursorInfo.height = " << cursorInfo.height;
    if (inputmethod_adapter_) {
      inputmethod_adapter_->OnCursorUpdate(cursorInfo);
    }
  }
}

void NWebInputMethodHandler::OnSelectionChanged(
    CefRefPtr<CefBrowser> browser,
    const CefString& text,
    const CefRange& selected_range) {
  whole_text_ = text;
  if (inputmethod_adapter_) {
    inputmethod_adapter_->OnSelectionChange(
        text.ToString16(), selected_range.from, selected_range.to);
  }
  {
    std::unique_lock<std::mutex> lock(textCursorMutex_);
    if (textCursorReady_ > 0) {
      textCursorReady_--;
    } else {
      LOG(INFO) << "OnSelectionChanged do not need notify_all";
      return;
    }
    if (textCursorReady_ == 0) {
      LOG(INFO) << "OnSelectionChanged notify_all";
      textCursorCv_.notify_all();
    }
  }
}

void NWebInputMethodHandler::SetIMEStatus(bool status) {
  if (browser_ != nullptr && browser_->GetHost() != nullptr) {
    CefRefPtr<CefTask> insert_task = new InputMethodTask(base::BindOnce(
        &NWebInputMethodHandler::SetIMEStatusOnUI, this, std::move(status)));
    browser_->GetHost()->PostTaskToUIThread(insert_task);
  }
}

void NWebInputMethodHandler::InsertText(const std::u16string& text) {
  if (text.empty()) {
    LOG(ERROR) << "insert text empty!";
    return;
  }

  if (browser_ != nullptr && browser_->GetHost() != nullptr) {
    CefRefPtr<CefTask> insert_task = new InputMethodTask(base::BindOnce(
        &NWebInputMethodHandler::InsertTextHandlerOnUI, this, std::move(text)));
    browser_->GetHost()->PostTaskToUIThread(insert_task);
  }
}

void NWebInputMethodHandler::DeleteBackward(int32_t length) {
  if (browser_ != nullptr && browser_->GetHost() != nullptr) {
    CefRefPtr<CefTask> delete_task = new InputMethodTask(base::BindOnce(
        &NWebInputMethodHandler::DeleteBackwardHandlerOnUI, this, length));
    browser_->GetHost()->PostTaskToUIThread(delete_task);
  }
}

void NWebInputMethodHandler::DeleteForward(int32_t length) {
  if (browser_ != nullptr && browser_->GetHost() != nullptr) {
    CefRefPtr<CefTask> delete_task = new InputMethodTask(base::BindOnce(
        &NWebInputMethodHandler::DeleteForwardHandlerOnUI, this, length));
    browser_->GetHost()->PostTaskToUIThread(delete_task);
  }
}

void NWebInputMethodHandler::SetIMEStatusOnUI(bool status) {
  if (!status && ime_text_composing_) {
    browser_->GetHost()->ImeFinishComposingText(false);
    ime_text_composing_ = false;
    composing_text_.clear();
  }
  ime_shown_ = status;
}

void NWebInputMethodHandler::InsertTextHandlerOnUI(const std::u16string& text) {
  if (text.empty()) {
    LOG(ERROR) << "insert text empty!";
    return;
  }
  {
    std::unique_lock<std::mutex> lock(textCursorMutex_);
    textCursorReady_++;
  }

  CefKeyEvent keyEvent;
  keyEvent.windows_key_code = ui::VKEY_PROCESSKEY;
  keyEvent.modifiers = 0;
  keyEvent.is_system_key = false;
  keyEvent.type = KEYEVENT_RAWKEYDOWN;
  browser_->GetHost()->SendKeyEvent(keyEvent);

  LOG(DEBUG) << "insert text length:" << text.length();
  if (text.length() > 1) {
    ResSchedClientAdapter::ReportScene(ResSchedStatusAdapter::WEB_SCENE_ENTER,
                                       ResSchedSceneAdapter::CLICK);
  }

  if (!ime_text_composing_) {
    ime_text_composing_ = true;
    composing_text_.clear();
  }
  composing_text_.append(text);
  browser_->GetHost()->ImeCommitText(composing_text_,
                                     CefRange(UINT32_MAX, UINT32_MAX), 0);

  // no selection
  ime_text_composing_ = false;
  selected_from_ += static_cast<int>(composing_text_.length());
  selected_to_ += static_cast<int>(composing_text_.length());
  composing_text_.clear();

  keyEvent.type = KEYEVENT_KEYUP;
  browser_->GetHost()->SendKeyEvent(keyEvent);
}

void NWebInputMethodHandler::DeleteForwardHandlerOnUI(int32_t length) {
  CefKeyEvent keyEvent;
  keyEvent.windows_key_code = ui::VKEY_DELETE;
  keyEvent.native_key_code = static_cast<int>(ScanKeyCode::DELETE_SCAN_CODE);
  keyEvent.modifiers = 0;
  keyEvent.is_system_key = false;
  keyEvent.character = keyEvent.unmodified_character = DEL_CHAR;

  if (!browser_ || !browser_->GetHost()) {
    LOG(ERROR) << "delete backward browser get failed";
    return;
  }
  is_need_notify_all_ = false;
  text_cursor_length_ = length;
  selected_from_ = selected_from_ >= whole_text_.size() ?  whole_text_.size() : selected_from_;
  if (whole_text_.substr(selected_from_).size() <= length) {
    text_cursor_length_ = whole_text_.substr(selected_from_).size();
    if (selected_from_ == 0) {
      is_need_notify_all_ = true;
    }
  }
  {
    std::unique_lock<std::mutex> lock(textCursorMutex_);
    textCursorReady_ += text_cursor_length_;
  }
  for (int32_t i = 0; i < length; i++) {
    keyEvent.type = KEYEVENT_RAWKEYDOWN;
    browser_->GetHost()->SendKeyEvent(keyEvent);
    keyEvent.type = KEYEVENT_CHAR;
    browser_->GetHost()->SendKeyEvent(keyEvent);
  }
  keyEvent.type = KEYEVENT_KEYUP;
  browser_->GetHost()->SendKeyEvent(keyEvent);
}

void NWebInputMethodHandler::DeleteBackwardHandlerOnUI(int32_t length) {
  CefKeyEvent keyEvent;
  keyEvent.windows_key_code = ui::VKEY_BACK;
  keyEvent.native_key_code = static_cast<int>(ScanKeyCode::BACKSPACE_SCAN_CODE);
  keyEvent.modifiers = 0;
  keyEvent.is_system_key = false;
  keyEvent.character = keyEvent.unmodified_character = DEL_CHAR;

  if (!browser_ || !browser_->GetHost()) {
    LOG(ERROR) << "delete forward browser get failed";
    return;
  }
  text_cursor_length_ = length;
  if (selected_from_ <= length) {
    text_cursor_length_ = selected_from_;
  }
  {
    std::unique_lock<std::mutex> lock(textCursorMutex_);
      textCursorReady_ += text_cursor_length_;
  }
  for (int32_t i = 0; i < length; i++) {
    keyEvent.type = KEYEVENT_RAWKEYDOWN;
    browser_->GetHost()->SendKeyEvent(keyEvent);
    keyEvent.type = KEYEVENT_CHAR;
    browser_->GetHost()->SendKeyEvent(keyEvent);
  }
  keyEvent.type = KEYEVENT_KEYUP;
  browser_->GetHost()->SendKeyEvent(keyEvent);
}

void NWebInputMethodHandler::SendEnterKeyEvent() {
  CefKeyEvent keyEvent;
  keyEvent.windows_key_code = ui::VKEY_RETURN;
  keyEvent.native_key_code = static_cast<int>(ScanKeyCode::ENTER_SCAN_CODE);

  keyEvent.type = KEYEVENT_KEYDOWN;
  keyEvent.character = '\r';
  keyEvent.modifiers = 0;
  keyEvent.is_system_key = false;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendKeyEvent(keyEvent);
  }

  keyEvent.type = KEYEVENT_CHAR;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendKeyEvent(keyEvent);
  }

  keyEvent.type = KEYEVENT_KEYUP;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendKeyEvent(keyEvent);
  }
}

void NWebInputMethodHandler::MoveCursor(const IMFAdapterDirection direction) {
  LOG(INFO) << "NWebInputMethodHandler::MoveCursor called";
  CefKeyEvent keyEvent;

  switch (direction) {
    case IMFAdapterDirection::UP: {
      keyEvent.windows_key_code = ui::VKEY_UP;
      keyEvent.native_key_code = static_cast<int>(ScanKeyCode::UP_SCAN_CODE);
      break;
    }
    case IMFAdapterDirection::LEFT: {
      keyEvent.windows_key_code = ui::VKEY_LEFT;
      keyEvent.native_key_code = static_cast<int>(ScanKeyCode::LEFT_SCAN_CODE);
      break;
    }
    case IMFAdapterDirection::RIGHT: {
      keyEvent.windows_key_code = ui::VKEY_RIGHT;
      keyEvent.native_key_code = static_cast<int>(ScanKeyCode::RIGHT_SCAN_CODE);
      break;
    }
    case IMFAdapterDirection::DOWN: {
      keyEvent.windows_key_code = ui::VKEY_DOWN;
      keyEvent.native_key_code = static_cast<int>(ScanKeyCode::DOWN_SCAN_CODE);
      break;
    }
    default: {
      LOG(ERROR) << "invalid direction";
      return;
    }
  }
  {
    std::unique_lock<std::mutex> lock(textCursorMutex_);
    textCursorReady_++;
  }
  keyEvent.type = KEYEVENT_KEYDOWN;
  keyEvent.modifiers = 0;
  keyEvent.is_system_key = false;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendKeyEvent(keyEvent);
  }

  keyEvent.type = KEYEVENT_KEYUP;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendKeyEvent(keyEvent);
  }
}

void NWebInputMethodHandler::SetScreenOffSet(double x, double y) {
  if (focus_status_) {
    if (focus_rect_status_ && (offset_x_ != x || offset_y_ != y)) {
      IMFAdapterCursorInfo cursorInfo{
          .left = (focus_rect_.x + focus_rect_.width) * device_pixel_ratio_ + x,
          .top = focus_rect_.y * device_pixel_ratio_ + y,
          .width = focus_rect_.width * device_pixel_ratio_,
          .height = focus_rect_.height * device_pixel_ratio_};
      LOG(DEBUG) << "NWebInputMethodHandler::SetScreenOffSet cursorInfo.left = "
                 << cursorInfo.left << ", cursorInfo.top = " << cursorInfo.top
                 << ", cursorInfo.width = " << cursorInfo.width
                 << ", cursorInfo.height = " << cursorInfo.height;
      if (inputmethod_adapter_) {
        inputmethod_adapter_->OnCursorUpdate(cursorInfo);
      }
    }
  }
  offset_x_ = x;
  offset_y_ = y;
}

void NWebInputMethodHandler::SetVirtualDeviceRatio(float device_pixel_ratio) {
  device_pixel_ratio_ = device_pixel_ratio;
}

void NWebInputMethodHandler::SetFocusStatus(bool focus_status) {
  if (focus_status && focus_rect_status_) {
    IMFAdapterCursorInfo cursorInfo{
        .left = (focus_rect_.x + focus_rect_.width) * device_pixel_ratio_ +
                offset_x_,
        .top = focus_rect_.y * device_pixel_ratio_ + offset_y_,
        .width = focus_rect_.width * device_pixel_ratio_,
        .height = focus_rect_.height * device_pixel_ratio_};
    LOG(DEBUG) << "NWebInputMethodHandler::SetFocusStatus cursorInfo.left = "
                << cursorInfo.left << ", cursorInfo.top = " << cursorInfo.top
                << ", cursorInfo.width = " << cursorInfo.width
                << ", cursorInfo.height = " << cursorInfo.height;
    if (inputmethod_adapter_) {
      inputmethod_adapter_->OnCursorUpdate(cursorInfo);
    }
  }
  focus_status_ = focus_status;
}

void NWebInputMethodHandler::OnEditableChanged(CefRefPtr<CefBrowser> browser,
                                               bool is_editable_node) {
  is_editable_node_ = is_editable_node;
}

bool NWebInputMethodHandler::GetIsEditableNode() {
  LOG(INFO) << "NWebInputMethodHandler is_editable_node_ = " << is_editable_node_;
  return is_editable_node_;
}

int32_t NWebInputMethodHandler::GetTextIndexAtCursor() {
  std::unique_lock<std::mutex> lock(textCursorMutex_);
  bool istextCursorReady = textCursorCv_.wait_for(
      lock, std::chrono::seconds(1), [this] { return textCursorReady_ == 0; });
  textCursorReady_ = 0;
  if (!istextCursorReady) {
    LOG(ERROR) << "GetTextIndexAtCursor wait_for timeout";
    if (ResetTextSelectiondata()) {
      return 0;
    }
  }
  return selected_to_;
}

std::u16string NWebInputMethodHandler::GetLeftTextOfCursor(int32_t number) {
  std::unique_lock<std::mutex> lock(textCursorMutex_);
  bool istextCursorReady = textCursorCv_.wait_for(
      lock, std::chrono::seconds(1), [this] { return textCursorReady_ == 0; });
  textCursorReady_ = 0;
  if (!istextCursorReady) {
    LOG(ERROR) << "GetLeftTextOfCursor wait_for timeout";
    if (ResetTextSelectiondata()) {
      return u"";
    }
  }
  int32_t selectBegin = selected_from_;
  int32_t selectEnd = selected_to_;
  if (!IsCorrectParam(number, selectBegin, selectEnd)) {
    return u"";
  }

  int32_t startPos = (number <= selectBegin ? (selectBegin - number) : 0);
  int32_t length = (number <= selectBegin ? number : selectBegin);
  return whole_text_.substr(startPos, length);
}

std::u16string NWebInputMethodHandler::GetRightTextOfCursor(int32_t number) {
  std::unique_lock<std::mutex> lock(textCursorMutex_);

  bool istextCursorReady = textCursorCv_.wait_for(
      lock, std::chrono::seconds(1), [this] { return textCursorReady_ == 0; });
  textCursorReady_ = 0;
  if (!istextCursorReady) {
    LOG(ERROR) << "GetRightTextOfCursor wait_for timeout";
    if (ResetTextSelectiondata()) {
      return u"";
    }
  }
  int32_t selectBegin = selected_from_;
  int32_t selectEnd = selected_to_;
  if (!IsCorrectParam(number, selectBegin, selectEnd)) {
    return u"";
  }
  return whole_text_.substr(selectEnd, number);
}

bool NWebInputMethodHandler::IsCorrectParam(int32_t number, int32_t& selectBegin, int32_t& selectEnd) {
  if (number < 0 || selectBegin < 0 || selectEnd < 0) {
      LOG(ERROR) << "param error, number:" << number << ", selectBegin:" << selectBegin << ", selectEnd:" << selectEnd;
      return false;
  }

  if (selectBegin > selectEnd) {
    std::swap(selectBegin, selectEnd);
  }
  if (selectEnd > whole_text_.size()) {
    LOG(ERROR) << "param error, end:" << selectEnd;
    return false;
  }
  return true;
}

bool NWebInputMethodHandler::ResetTextSelectiondata()
{
  if (is_need_notify_all_) {
    whole_text_ = u"";
    selected_from_ = 0;
    selected_to_ = 0;
    textCursorReady_ = 0;
    is_need_notify_all_ = false;
    return true;
  }
  return false;
}
}  // namespace OHOS::NWeb
